home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / ip / ka9q / alpha.arc / DIRUTIL.C < prev    next >
C/C++ Source or Header  |  1988-02-16  |  13KB  |  499 lines

  1. /* dirutil.c - MS-DOS directory reading routines
  2.  *
  3.  * Bdale Garbee, N3EUA, Dave Trulli, NN2Z, and Phil Karn, KA9Q
  4.  * Directory sorting by Mike Chepponis, K3MC
  5.  */
  6.  
  7. #include <stdio.h>
  8. #include <stat.h>
  9. #include "global.h"
  10.  
  11. #ifndef    FALSE
  12. #define    FALSE    (0)
  13. #endif
  14.  
  15. #ifndef    TRUE
  16. #define    TRUE    !(FALSE)
  17. #endif
  18.  
  19. #define REGFILE    (ST_HIDDEN|ST_SYSTEM|ST_DIRECT)
  20. #define    SET_DTA        0x1a
  21. #define    FIND_FIRST    0x4e
  22. #define    FIND_NEXT    0x4f
  23.  
  24. struct dirent {
  25.     char rsvd[21];
  26.     char attr;
  27.     short ftime;
  28.     short fdate;
  29.     long fsize;
  30.     char fname[13];
  31. };
  32. #define    NULLENT    (struct dirent *)0
  33.  
  34. struct dirsort {
  35.     struct dirsort *prev;
  36.     struct dirsort *next;
  37.     struct dirent *direntry;
  38. };
  39. #define    NULLSORT (struct dirsort *)0
  40.  
  41. /* Create a directory listing in a temp file and return the resulting file
  42.  * descriptor. If full == 1, give a full listing; else return just a list
  43.  * of names.
  44.  */
  45. FILE *
  46. dir(path,full)
  47. char *path;
  48. int full;
  49. {
  50.     FILE *fp,*tmpfile();
  51.  
  52.     fp = tmpfile();
  53.     getdir(path,full,fp);
  54.     /* This should be rewind(), but Aztec doesn't have it */
  55.     fseek(fp,0L,0);
  56.     return fp;
  57. }
  58.  
  59. /* wildcard filename lookup */
  60. filedir(name,times,ret_str)
  61. char *name;
  62. int times;
  63. char *ret_str;
  64. {
  65.     register char *cp,*cp1;
  66.     static struct dirent sbuf;
  67.  
  68.     bdos(SET_DTA,&sbuf);    /* Set disk transfer address */
  69.     /* Find matching file */
  70.     if(dos(times == 0 ? FIND_FIRST:FIND_NEXT,0,REGFILE,name,0,0) == -1) 
  71.         sbuf.fname[0] = '\0';
  72.  
  73.     /* Copy result to output, forcing to lower case */
  74.     for(cp = ret_str,cp1 = sbuf.fname; cp1 < &sbuf.fname[13] && *cp1 != '\0';)
  75.         *cp++ = tolower(*cp1++);
  76.     *cp = '\0';
  77. }
  78. /* do a directory list to the stream 
  79.  * full = 0 -> short form, 1 is long
  80. */
  81. getdir(path,full,file)
  82. char *path;
  83. int full;
  84. FILE *file;
  85. {
  86.     struct dirent sbuf;
  87.     struct stat statbuf;
  88.     register char *cp,*cp1;
  89.     char dirtmp[20];
  90.     int command = FIND_FIRST;
  91.     int i = 0;
  92.     int cflag = 0;
  93.  
  94.     char temp_name[13];        /* Holds a filename temporarily */
  95.     int loop_counter;
  96.     int n = 0;
  97.     char line_buf[50];        /* for long dirlist */
  98.     char cbuf[20],cbuf1[20];    /* for making line_buf */
  99.     
  100.     struct dirsort *prev, *head, *here, *new;
  101.     struct dirent *de;
  102.     void dir_sort(), format_fname(), commas(), isfree(), free_clist();
  103.     unsigned short ax,bx,cx,dx;
  104.     unsigned long free_bytes, total_bytes;
  105.     char s_free[11], s_total[11];
  106.     int malloc_lost = FALSE;
  107.  
  108.     /* Root directory is a special case */
  109.     if(path == NULLCHAR || *path == '\0' || strcmp(path,"\\") == 0)
  110.         path = "\\*.*";
  111.  
  112.     /* If arg is a directory, append "\*.*" to it.
  113.      * This is tricky, since the "stat" system call actually
  114.      * calls the DOS "find matching file" function. The stat
  115.      * call therefore returns the attributes for the first matching
  116.      * entry in the directory. If the arg already ends in *.*,
  117.      * stat will match the . entry in the directory and indicate
  118.      * that the argument is a valid directory name. Hence the
  119.      * heuristic check for '*' in the file name. Kludge...
  120.      */
  121.     else if(index(path,'*') == NULLCHAR
  122.      && stat(path,&statbuf) != -1
  123.      && (statbuf.st_attr & ST_DIRECT)) {
  124.         if((cp = malloc(strlen(path) + 10)) == NULLCHAR)
  125.             return -1;
  126.         sprintf(cp,"%s%c%s",path,'\\',"*.*");
  127.         path = cp;
  128.         cflag = 1;
  129.     }
  130.     head = NULLSORT;    /* No head of chain yet... */
  131.     for(;;){
  132.         bdos(SET_DTA,&sbuf);    /* Set disk transfer address */
  133.         if(dos(command, 0, REGFILE, path, 0, 0) == -1)
  134.             break;
  135.  
  136.         command = FIND_NEXT;    /* Got first one already... */
  137.         if(sbuf.fname[0] != '.'){
  138.             /* nuke "." and ".." */
  139.             n++;    /* One more entry */
  140.             new = (struct dirsort *) malloc(sizeof(struct dirsort));
  141.             if(new == NULLSORT)
  142.                 malloc_lost = TRUE;
  143.             de  = (struct dirent *)malloc(sizeof(struct dirent));
  144.             if(de == NULLENT)
  145.                 malloc_lost = TRUE;
  146.             if(malloc_lost){
  147.                 /* Clean up and call other routine */
  148.                 if(new)free(new);
  149.                 free_clist(head);
  150.                 return getdir_nosort(path,full,file);
  151.             }
  152.             *de = sbuf;    /* Copy contents of directory entry struct */
  153.  
  154.             /* Fix up names for easier sorting... pain! */
  155.             strcpy(de->fname,"           ");    /* 11 blanks */
  156.             cp  = sbuf.fname;
  157.             cp1 = de->fname;
  158.             do *cp1++ = *cp++; while (*cp && *cp != '.');
  159.             if(*cp++){
  160.                 /* If there is an extension */
  161.                 cp1 = &(de->fname[8]);
  162.                 do *cp1++ = *cp++; while (*cp);
  163.             }
  164.             if(!(int)head){
  165.                 /* Make the first one */
  166.                 here = head = new;
  167.                 head->prev = head->next = NULLSORT;
  168.             } else {
  169.                 /* Link on next one */
  170.                 new->next = NULLSORT;
  171.                 new->prev = here;
  172.                 here->next = new;
  173.                 here = new;
  174.             }
  175.             new->direntry = de;
  176.         } /* IF on "." */
  177.     } /* infinite FOR loop */
  178.  
  179.     if(head)
  180.         dir_sort(head);        /* Make a nice, sorted list */
  181.  
  182.     here = head;
  183.     if(here && full){
  184.         do {
  185.             sbuf = *(here->direntry); /* Copy data for printing */
  186.             format_fname(dirtmp,sbuf.fname,sbuf.attr);
  187.  
  188.             /* Long form, give other info too */
  189.             sprintf(line_buf,"%-13s",dirtmp);
  190.             if(sbuf.attr & ST_DIRECT)
  191.                 strcat(line_buf,"           ");/* 11 spaces */
  192.             else {
  193.                 sprintf(cbuf,"%ld",sbuf.fsize);
  194.                 commas(cbuf);
  195.                 sprintf(cbuf1,"%10s ",cbuf);
  196.                 strcat(line_buf,cbuf1);    /* Do filesize */
  197.             }
  198.             sprintf(cbuf,"%2d:%02d %2d/%02d/%02d%s",
  199.               (sbuf.ftime >> 11) & 0x1f,    /* hour */
  200.               (sbuf.ftime >> 5) & 0x3f,    /* minute */
  201.               (sbuf.fdate >> 5) & 0xf,    /* month */
  202.               (sbuf.fdate ) & 0x1f,        /* day */
  203.               (sbuf.fdate >> 9) + 80,    /* year */
  204.               (i^=1) ? "   " : "\n");
  205.             strcat(line_buf,cbuf);
  206.             fprintf(file,"%s",line_buf);
  207.         } while (here = here->next);
  208.         if(i & 1)
  209.             fprintf(file,"\n");
  210.     }
  211.     else if(here){
  212.         /* This is the short form; make sure >=1 entry */
  213.         do {
  214.             sbuf = *(here->direntry); /* Copy data for printing */
  215.             format_fname(dirtmp,sbuf.fname,sbuf.attr);
  216.             fprintf(file,"%s\n",dirtmp);
  217.         } while (here = here->next);
  218.     }
  219.     /* Give back all the memory we temporarily needed... */
  220.     free_clist(head);
  221.  
  222.     if(full){
  223.         /* Provide additional information only on DIR */
  224.         ax = 0x3600;    /* AH = 36h, AL = 0 (AL not used) */
  225.         dx = 0;        /* Default drive */
  226.         isfree(&ax,&bx,&cx,&dx);
  227.  
  228.         free_bytes  = (unsigned long)ax * (unsigned long)cx;
  229.         total_bytes = free_bytes * (unsigned long)dx;
  230.         free_bytes *= (unsigned long)bx;
  231.  
  232.         sprintf(s_free,"%ld",free_bytes);    commas(s_free);
  233.         sprintf(s_total,"%ld",total_bytes);    commas(s_total);
  234.  
  235.  
  236.         if(n)
  237.             sprintf(cbuf,"%d",n);
  238.         else
  239.             strcpy(cbuf,"No");
  240.  
  241.         fprintf(file,"%s file%s. %s bytes free. Disk size %s bytes.\n",
  242.             cbuf,(n==1? "":"s"),s_free,s_total);
  243.     }
  244.     if(cflag)
  245.         free(path);
  246.     return 0;
  247. }
  248. /* Change working directory */
  249. docd(argc,argv)
  250. int argc;
  251. char *argv[];
  252. {
  253.     char dirname[128],*getcwd();
  254.  
  255.     if(argc > 1){
  256.         if(chdir(argv[1]) == -1){
  257.             printf("Can't change directory\n");
  258.             return 1;
  259.         }
  260.     }
  261.     if(getcwd(dirname,0) != NULLCHAR){
  262.         printf("\\%s\n",dirname);
  263.     }
  264.     return 0;
  265. }
  266. /* List directory to console */
  267. dodir(argc,argv)
  268. int argc;
  269. char *argv[];
  270. {
  271.     char *path;
  272.  
  273.     if(argc >= 2){
  274.         path = argv[1];
  275.     } else {
  276.         path = "*.*";
  277.     }
  278.     getdir(path,1,stdout);
  279.     return 0;
  280. }
  281.  
  282.  
  283. /*
  284.  * Return a string with commas every 3 positions.
  285.  * If malloc() fails, return original string unmodified.
  286.  * else the original string is replace with the string with commas.
  287.  *
  288.  * The caller must be sure that there is enough room for the resultant
  289.  * string.
  290.  *
  291.  *
  292.  * k3mc 4 Dec 87
  293.  */
  294. void
  295. commas(dest)
  296. char *dest;
  297. {
  298.     char *src, *core;    /* Place holder for malloc */
  299.     unsigned cc;        /* The comma counter */
  300.     unsigned len;
  301.     
  302.     len = strlen(dest);
  303.     if( (core = src = (char *)malloc(len+1)) == NULLCHAR)
  304.         return;
  305.  
  306.     strcpy(src,dest);    /* Make a copy, so we can muck around */
  307.     cc = (len-1)%3 + 1;    /* Tells us when to insert a comma */
  308.     
  309.     while(*src != '\0'){
  310.         *dest++ = *src++;
  311.         if( ((--cc) == 0) && *src ){
  312.             *dest++ = ','; cc = 3;
  313.         }
  314.     }
  315.     free(core);
  316.     *dest = '\0';
  317. }
  318. /*
  319.  * This insertion sort adapted from "Writing Efficient Programs" by Jon Louis
  320.  * Bentley, Prentice-Hall 1982, ISBN 0-13-070244-X (paperback) p. 65
  321.  *
  322.  * Run Time (sec) = K * N^2, where K = 21e-6 on my turbo XT clone (SI=2.6).
  323.  * This could be improved to perhaps K * N * log2(N) using Quicksort, but,
  324.  * as Bentley points out, this insertion sort is actually faster for small
  325.  * values of N.  His "best" sorting algorithm uses an insertion sort/Quicksort
  326.  * hybrid, with the "cutoff" value being about 30 elements.
  327.  *
  328.  * I have opted for the straight insertion sort because it is quite simple,
  329.  * provably correct, and not a terrible performer when N < 1000, which is the
  330.  * case for most directories.
  331.  */
  332. void
  333. dir_sort(head)
  334. struct dirsort *head;
  335. {
  336.     struct dirsort *here, *backtrack;
  337.     struct dirent *de_temp;
  338.     
  339.     for(here = head->next; here != NULLSORT; here = here->next){
  340.         backtrack = here;
  341.         de_temp = here->direntry;
  342.         while(backtrack->prev
  343.          && strcmp(de_temp->fname,backtrack->prev->direntry->fname)<0){
  344.             backtrack->direntry = backtrack->prev->direntry;
  345.             backtrack = backtrack->prev;
  346.         }
  347.         backtrack->direntry = de_temp;
  348.     }
  349. }
  350.  
  351. void
  352. format_fname(dest,src,attr)
  353. char    *dest, *src;
  354. char    attr;
  355. {
  356. char    *cp = src+8;
  357. int    loop_counter;
  358.  
  359.     for(loop_counter=0; loop_counter<8; loop_counter++){
  360.         *dest++ = tolower(*src++);
  361.         if(*src == ' ')break;
  362.     }
  363.     if(strcmp(cp,"   ") != 0){    /* There is an extension */
  364.         *dest++ = '.';
  365.         for(loop_counter=0; loop_counter<3; loop_counter++){
  366.         *dest++ = tolower(*cp++);
  367.             if(*cp == ' ')break;
  368.         }
  369.     }
  370.     if(attr & ST_DIRECT)*dest++ = '\\';
  371.     *dest = '\0';
  372. }
  373. void
  374. free_clist(head)
  375. struct dirsort *head;
  376. {
  377.     struct dirsort *here;
  378.     
  379.     here = head;
  380.     if(here)do{
  381.         free(here->direntry);
  382.         if(head != here){
  383.             free(head);
  384.             head = here;
  385.         }
  386.     } while (here = here->next);
  387.     if(head != here){
  388.         free(head);
  389.         head = here;
  390.     }
  391. }
  392.  
  393.  
  394.  
  395. getdir_nosort(path,full,file)
  396. char *path;
  397. int full;
  398. FILE *file;
  399. {
  400.     struct dirent sbuf;
  401.     struct stat statbuf;
  402.     register char *cp,*cp1;
  403.     char dirtmp[20];
  404.     int command = FIND_FIRST;
  405.     int i = 0;
  406.     int cflag = 0;
  407.  
  408.     char    line_buf[50];        /* for long dirlist */
  409.     char    cbuf[20],cbuf1[20];    /* for making line_buf */
  410.     
  411.     void format_fname(),commas(),isfree();
  412.     unsigned short ax,bx,cx,dx;
  413.     unsigned long free_bytes, total_bytes;
  414.     char s_free[11], s_total[11];
  415.     int n = 0;    /* Number of directory entries */
  416.     
  417.     /* Root directory is a special case */
  418.     if(path == NULLCHAR || *path == '\0' || strcmp(path,"\\") == 0)
  419.         path = "\\*.*";
  420.  
  421.     /* If arg is a directory, append "\*.*" to it.
  422.      * This is tricky, since the "stat" system call actually
  423.      * calls the DOS "find matching file" function. The stat
  424.      * call therefore returns the attributes for the first matching
  425.      * entry in the directory. If the arg already ends in *.*,
  426.      * stat will match the . entry in the directory and indicate
  427.      * that the argument is a valid directory name. Hence the
  428.      * heuristic check for '*' in the file name. Kludge...
  429.      */
  430.     else if(index(path,'*') == NULLCHAR
  431.      && stat(path,&statbuf) != -1
  432.      && (statbuf.st_attr & ST_DIRECT)) {
  433.         if((cp = malloc(strlen(path) + 10)) == NULLCHAR)
  434.             return -1;
  435.         sprintf(cp,"%s%c%s",path,'\\',"*.*");
  436.         path = cp;
  437.         cflag = 1;
  438.     }
  439.     for(;;){
  440.         bdos(SET_DTA,&sbuf);    /* Set disk transfer address */
  441.         if(dos(command,0,REGFILE,path,0,0) == -1)
  442.             break;
  443.  
  444.         command = FIND_NEXT;    /* Got first one already... */
  445.         if(sbuf.fname[0] != '.'){    /* nuke "." and ".." */
  446.             if(full){
  447.                 n++;    /* Count 'em */
  448.                 format_fname(dirtmp,sbuf.fname,sbuf.attr);
  449.                 sprintf(line_buf,"%-13s",dirtmp);
  450.                 if(sbuf.attr & ST_DIRECT){
  451.                     strcat(line_buf,"           ");/* 11 spaces */
  452.                 } else {
  453.                     sprintf(cbuf,"%ld",sbuf.fsize);
  454.                     commas(cbuf);
  455.                     sprintf(cbuf1,"%10s ",cbuf);
  456.                     strcat(line_buf,cbuf1);    /* Do filesize */
  457.                 }
  458.                 sprintf(cbuf,"%2d:%02d %2d/%02d/%02d%s",
  459.                   (sbuf.ftime >> 11) & 0x1f,    /* hour */
  460.                   (sbuf.ftime >> 5) & 0x3f,    /* minute */
  461.                   (sbuf.fdate >> 5) & 0xf,    /* month */
  462.                   (sbuf.fdate ) & 0x1f,        /* day */
  463.                   (sbuf.fdate >> 9) + 80,    /* year */
  464.                   (i^=1) ? "   " : "\n");
  465.                 strcat(line_buf,cbuf);
  466.                 fprintf(file,"%s",line_buf);
  467.             } else  {    /* is short form */
  468.                 format_fname(dirtmp,sbuf.fname,sbuf.attr);
  469.                 fprintf(file,"%-13s\n",dirtmp);
  470.             }
  471.         }
  472.     }
  473.     if(full){
  474.         if(i)
  475.             fprintf(file,"\n");
  476.         ax = 0x3600;    /* AH = 36h, AL = 0 (AL not used) */
  477.         dx = 0;        /* Default drive */
  478.         isfree(&ax,&bx,&cx,&dx);
  479.         free_bytes  = (unsigned long)ax * (unsigned long)cx;
  480.         total_bytes = free_bytes * (unsigned long)dx;
  481.         free_bytes *= (unsigned long)bx;
  482.         sprintf(s_free,"%ld",free_bytes);
  483.         commas(s_free);
  484.         sprintf(s_total,"%ld",total_bytes);
  485.         commas(s_total);
  486.         if(n)
  487.             sprintf(cbuf,"%d",n);
  488.         else
  489.             strcpy(cbuf,"No");
  490.  
  491.         fprintf(file,"%s file%s. %s bytes free. Disk size %s bytes.\n",
  492.          cbuf,(n==1? "":"s"),s_free,s_total);
  493.     }
  494.     if(cflag)
  495.         free(path);
  496.     return 0;
  497. }
  498.  
  499.